// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2025-02-08

package nod

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/go-libs/log"
	"github.com/valyala/fasthttp"
	"integration/app/errs"
	"integration/app/lib/conf"
	"integration/app/lib/nod/api"
	"integration/app/lib/nod/customer"
	"integration/app/lib/nod/sell_order"
	"integration/app/lib/nod/sell_refund"
	"net/http"
)

var (
	// Api
	// 是一个单例, 用于以 HTTP 方式与 NodCloud/V8 进行数据交互.
	//
	//   nod.Api.GetOrder(ctx, 100)
	Api ErpApi
)

type (
	// ErpApi
	// 数据交互接口.
	ErpApi interface {
		// GetGoodsList
		// 读取商品列表.
		//
		//   nod.Api.GetGoodsList(ctx, 1, 25)
		GetGoodsList(ctx context.Context, agent *conf.ErpAgent, page, size int) (res *api.GetGoodsListResponse, err error)

		// PostCustomer
		// 上报客户资料.
		//
		//   nod.Api.PostCustomer(ctx, agent, data)
		PostCustomer(ctx context.Context, agent *conf.ErpAgent, data *customer.Entity) (res *api.PostCustomerResponse, err error)

		// PostSellOrder
		// 上报销售订单.
		//
		//   nod.Api.PostSellOrder(ctx, agent, data)
		PostSellOrder(ctx context.Context, agent *conf.ErpAgent, data *sell_order.Entity) (res *api.PostSellOrderResponse, err error)

		// PostSellRefund
		// 上报销售退单.
		//
		//   nod.Api.PostSellRefund(ctx, agent, data)
		PostSellRefund(ctx context.Context, agent *conf.ErpAgent, data *sell_refund.Entity) (res *api.PostSellRefundResponse, err error)
	}

	ErpApiRequest interface {
		GetRequestUrl() string
	}

	ErpApiResponse interface {
		GetError() (err error)
	}

	ea struct{}
)

// GetGoodsList
// 读取商品列表.
func (o *ea) GetGoodsList(ctx context.Context, agent *conf.ErpAgent, page, size int) (*api.GetGoodsListResponse, error) {
	req := api.NewGetGoodsListRequest(page, size)
	res := api.NewGetGoodsListResponse()
	err := o.get(ctx, agent, req, res)
	return res, err
}

// PostCustomer
// 上报客户资料.
func (o *ea) PostCustomer(ctx context.Context, agent *conf.ErpAgent, data *customer.Entity) (res *api.PostCustomerResponse, err error) {
	var req = api.NewPostCustomerRequest()
	res = api.NewPostCustomerResponse()
	err = o.post(ctx, agent, req, res, data)
	return
}

// PostSellOrder
// 上报销售订单.
func (o *ea) PostSellOrder(ctx context.Context, agent *conf.ErpAgent, data *sell_order.Entity) (res *api.PostSellOrderResponse, err error) {
	var req = api.NewPostSellOrderRequest()
	res = api.NewPostSellOrderResponse()
	err = o.post(ctx, agent, req, res, data)
	return
}

// PostSellRefund
// 上报销售退单.
func (o *ea) PostSellRefund(ctx context.Context, agent *conf.ErpAgent, data *sell_refund.Entity) (res *api.PostSellRefundResponse, err error) {
	var req = api.NewPostSellRefundRequest()
	res = api.NewPostSellRefundResponse()
	err = o.post(ctx, agent, req, res, data)
	return
}

// 执行GET请求.
func (o *ea) get(ctx context.Context, agent *conf.ErpAgent, req ErpApiRequest, res ErpApiResponse) (err error) {
	var (
		buf      []byte
		cli      *conf.ErpClient
		has      bool
		request  = fasthttp.AcquireRequest()
		response = fasthttp.AcquireResponse()
		method   = http.MethodGet
		url      string
	)

	if cli, has = conf.Config.GetClient(agent.ClientKey); !has {
		err = errs.ErrErpClientNotConfigured
		return
	} else {
		url = fmt.Sprintf(`%s%s`, cli.Host, req.GetRequestUrl())
	}

	log.Infofc(ctx, `[nod:api] http request begin: method="%s", url="%s"`, method, url)

	// 1. 监听结束.
	defer func() {
		// 1.1 捕获异常.
		if r := recover(); r != nil {
			log.Fatalfc(ctx, `[nod:api] http request panic: fatal="%v"`, r)
		} else if err != nil {
			log.Errorfc(ctx, `[nod:api] http request failed: error="%v"`, err)
		} else {
			log.Infofc(ctx, `[nod:api] http request end: %s`, buf)
		}

		// 1.2 释放资源.
		fasthttp.ReleaseRequest(request)
		fasthttp.ReleaseResponse(response)
	}()

	// 2. 请求参数.
	request.Header.SetContentType(`application/json`)
	request.Header.Set(`Authorization`, cli.Authorization)
	request.Header.SetMethod(method)
	request.Header.SetRequestURI(url)

	// 2.1 调用链.
	for k, v := range log.ContextHeader(ctx) {
		if len(v) > 0 && v[0] != "" {
			request.Header.Set(k, v[0])
		}
	}

	// 3. 发送请求.
	if err = fasthttp.DoTimeout(request, response, cli.TimeoutDuration); err != nil {
		return
	}

	// 3.1 结果过滤.
	if status := response.StatusCode(); status != http.StatusOK {
		err = fmt.Errorf(`HTTP %d %s`, status, http.StatusText(status))
		return
	}

	// 3.2 解析结果.
	buf = response.Body()
	if err = json.Unmarshal(buf, res); err != nil {
		return
	}

	// 3.3 业务错误.
	err = res.GetError()
	return
}

// 执行POST请求.
func (o *ea) post(ctx context.Context, agent *conf.ErpAgent, req ErpApiRequest, res ErpApiResponse, data any) (err error) {
	var (
		body, buf []byte
		cli       *conf.ErpClient
		has       bool
		request   *fasthttp.Request
		response  *fasthttp.Response
		method    = http.MethodPost
		url       string
	)

	if cli, has = conf.Config.GetClient(agent.ClientKey); !has {
		err = errs.ErrErpClientNotConfigured
		return
	} else {
		url = fmt.Sprintf(`%s%s`, cli.Host, req.GetRequestUrl())
	}

	// 1. 解析入参.
	if body, err = json.Marshal(data); err != nil {
		log.Infofc(ctx, `[nod:api] http request aborted: error="%v"`, err)
		return
	} else {
		request = fasthttp.AcquireRequest()
		response = fasthttp.AcquireResponse()
		log.Infofc(ctx, `[nod:api] http request begin: method="%s", url="%s", body=%s`, method, url, body)
	}

	// 2. 监听结束.
	defer func() {
		// 2.1 捕获异常.
		if r := recover(); r != nil {
			log.Fatalfc(ctx, `[nod:api] http request panic: fatal="%v"`, r)
		} else if err != nil {
			log.Errorfc(ctx, `[nod:api] http request failed: error="%v"`, err)
		} else {
			log.Infofc(ctx, `[nod:api] http request end: %s`, buf)
		}

		// 2.2 释放资源.
		fasthttp.ReleaseRequest(request)
		fasthttp.ReleaseResponse(response)
	}()

	// 3. 请求参数.
	request.Header.SetContentType(`application/json`)
	request.Header.Set(`Authorization`, cli.Authorization)
	request.Header.SetMethod(method)
	request.Header.SetRequestURI(url)
	request.SetBodyRaw(body)

	// 3.1 调用链.
	for k, v := range log.ContextHeader(ctx) {
		if len(v) > 0 && v[0] != "" {
			request.Header.Set(k, v[0])
		}
	}

	// 4. 发送请求.
	if err = fasthttp.DoTimeout(request, response, cli.TimeoutDuration); err != nil {
		return
	}

	// 4.1 结果过滤.
	if status := response.StatusCode(); status != http.StatusOK {
		err = fmt.Errorf(`HTTP %d %s`, status, http.StatusText(status))
		return
	}

	// 4.2 解析结果.
	buf = response.Body()
	if err = json.Unmarshal(buf, res); err != nil {
		return
	}

	// 4.3 业务错误.
	err = res.GetError()
	return
}

func (o *ea) init() *ea { return o }
